//-< MAIN.CPP >------------------------------------------------------*--------*
// GigaBASE                  Version 1.0         (c) 1999  GARRET    *     ?  *
// (Post Relational Database Management System)                      *   /\|  *
//                                                                   *  /  \  *
//                          Created:     22-Jan-2012   K.A. Knizhnik * / [] \ *
//                          Last update: 22-Jan-2012   K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Interactive tool for StockDB
//-------------------------------------------------------------------*--------*

#include "stockdb.h"

static char const* commandsList = 
    "Commands:\n"
    "\tadd symbol csv_file\n"
    "\tget symbol from till [csv_file]\n"
    "\tdel symbol from till\n"
    "\tapply [processor symbol from till [csv_file]]\n"
    "\tlist [symbols]\n"
    "\tload [file-with-symbols [from [till]]]\n"
    "\tloadsym symbol [from [till]]]\n"
    "\tcommit\n"
    "\tbackup file [compact]\n"
    "\thelp\n"
    "\texit\n";


bool execCommand(StockDB& db, int argc, char* argv[])
{
    if (strcmp(argv[0], "add") == 0) { 
        if (argc != 3) {
            fprintf(stderr, "Add command expects two argments: symbol and file_name\n");
        } else { 
            size_t nQuotes;
            if (db.importQuotes(argv[1], argv[2], nQuotes) != OK) { 
                fprintf(stderr, "Failed to import quotes: %s\n", db.getErrorMessage().c_str());
                return false;
            }
            printf("Import %d quotes of symbol %s\n", int(nQuotes), argv[1]);
            return true;
        }            
    } else if (strcmp(argv[0], "del") == 0) { 
        if (argc != 4) {
            fprintf(stderr, "Del command expects three argments: symbol, from, till\n");
            return false;
        } else { 
            date_t from, till;
            if (!StockDB::parseDate(from, argv[2])) {
                fprintf(stderr, "Invalid from date: %s\n", argv[2]);
                return false;
            }
            if (!StockDB::parseDate(till, argv[3])) { 
                fprintf(stderr, "Invalid till date: %s\n", argv[3]);
                return false;
            }
            printf("Delete %d quotes\n", db.removeQuotes(argv[1], from, till));
            return true;
        }      
    } else if (strcmp(argv[0], "get") == 0) { 
        if (argc != 4 && argc != 5) {
            fprintf(stderr, "Get command expects three or four argments: symbol, from, till [,csv-file]\n");
            return false;
        } else { 
            date_t from, till;
            if (!StockDB::parseDate(from, argv[2])) { 
                fprintf(stderr, "Invalid from date: %s\n", argv[2]);
                return false;
            }
            if (!StockDB::parseDate(till, argv[3])) { 
                fprintf(stderr, "Invalid till date: %s\n", argv[3]);
                return false;
            }
            if (argc == 5) { 
                if (db.exportQuotes(argv[1], from, till, argv[4]) != OK) { 
                    fprintf(stderr, "Failed to export quotes: %s\n", db.getErrorMessage().c_str());
                    return false;
                }
            } else { 
                if (db.exportQuotes(argv[1], from, till, stdout) != OK) {
                    fprintf(stderr, "Failed to export quotes: %s\n", db.getErrorMessage().c_str());
                    return false;
                }
            }
        }
    } else if (strcmp(argv[0], "apply") == 0) { 
        if (argc == 1) {
            printf("List of available processors:\n");
            for (map<string, Processor*>::const_iterator i = Processor::processors.begin(); i != Processor::processors.end(); ++i) { 
                printf("\t%s\n", i->first.c_str());
            }            
        } else if (argc != 5 && argc != 6) {
            fprintf(stderr, "Apply command expects four or five argments: processor, symbol, from, till [,csv-file]\n");
            return false;
        } else { 
            date_t from, till;
            if (!StockDB::parseDate(from, argv[3])) { 
                fprintf(stderr, "Invalid from date: %s\n", argv[3]);
                return false;
            }
            if (!StockDB::parseDate(till, argv[4])) { 
                fprintf(stderr, "Invalid till date: %s\n", argv[4]);
                return false;
            }
            FILE* out = stdout;
            if (argc == 6) { 
                out = fopen(argv[5], "w");
                if (out == NULL) { 
                    fprintf(stderr, "Failed to create file '%s'\n", argv[5]);
                    return false;
                }
            }
            if (db.processQuotes(argv[1], argv[2], from, till, out) != OK) { 
                fprintf(stderr, "Processor %s not found\n", argv[1]);
                return false;
            }
            if (argc == 6) { 
                fclose(out);
            }
        }
    } else if (strcmp(argv[0], "load") == 0) { 
        if (argc > 4) {
            fprintf(stderr, "Load command expects up to three argments: file_with_symbols, from, till\n");
            return false;
        }
        ErrorCode err;
        if (argc > 1) {
            date_t from = 0, till = 0;
            if (argc > 2) { 
                if (!StockDB::parseDate(from, argv[2])) { 
                    fprintf(stderr, "Invalid from date: %s\n", argv[2]);
                    return false;
                }
                if (argc > 3) {             
                    if (!StockDB::parseDate(till, argv[3])) { 
                        fprintf(stderr, "Invalid till date: %s\n", argv[3]);
                        return false;
                    }
                }
            }
            err = db.load(argv[1], from, till);
        } else { 
            err = db.update();
        }
        if (err != OK) { 
            fprintf(stderr, "Failed to load quotes: %s\n",  db.getErrorMessage().c_str());
            return false;
        }                        
    } else if (strcmp(argv[0], "loadsym") == 0) { 
        if (argc > 4) {
            fprintf(stderr, "Loadsym command expects up to three argments: symbol, from, till\n");
            return false;
        }
        ErrorCode err;
        date_t from = 0, till = 0;
        if (argc > 2) { 
            if (!StockDB::parseDate(from, argv[2])) { 
                fprintf(stderr, "Invalid from date: %s\n", argv[2]);
                return false;
            }
            if (argc > 3) {             
                if (!StockDB::parseDate(till, argv[3])) { 
                    fprintf(stderr, "Invalid till date: %s\n", argv[3]);
                    return false;
                }
            }
        }
        err = db.loadSymbol(argv[1], from, till);
        if (err != OK) { 
            fprintf(stderr, "Failed to load quotes: %s\n",  db.getErrorMessage().c_str());
            return false;
        }                        
    } else if (strcmp(argv[0], "list") == 0) { 
        char timeBuf[2][TIME_BUF_SIZE];

        if (argc == 1) { 
            vector<Stock> stocks = db.getStocks();
            for (size_t i = 0; i < stocks.size(); i++) { 
                Stock const& stock = stocks[i];
                printf("%s: %s..%s [%d quotes]\n", stock.name.c_str(), StockDB::printDate(stock.from, timeBuf[0], sizeof timeBuf[0]), StockDB::printDate(stock.till, timeBuf[1], sizeof timeBuf[1]), stock.nQuotes);
            }
        } else {  
            for (int i = 1; i < argc; i++) { 
                Stock stock = db.getStock(argv[i]);
                printf("%s: %s..%s [%d quotes]\n", stock.name.c_str(), StockDB::printDate(stock.from, timeBuf[0], sizeof timeBuf[0]), StockDB::printDate(stock.till, timeBuf[1], sizeof timeBuf[1]), stock.nQuotes);
            }     
         }      
    } else if (strcmp(argv[0], "commit") == 0) { 
        if (argc > 1) {
            fprintf(stderr, "Commit command has not arguments\n");
            return false;
        }
        db.flush();
    } else if (strcmp(argv[0], "backup") == 0) { 
        if (argc < 2 || argc > 3) {
            fprintf(stderr, "Backup command expectes one mandatory argument (path to backup file) and one optional - compact\n");
            return false;
        }
        if (!db.backup(argv[1], argc > 2 && strcmp(argv[2], "compact") == 0 ? true : false)) { 
            fprintf(stderr, "Backup failed\n");
        }
    } else if (strcmp(argv[0], "help") == 0) { 
        fputs(commandsList, stdout);
    } else { 
        fprintf(stderr, "Unknown command '%s'\n", argv[0]);
        return false;
    }
    return true;
}

int main(int argc, char* argv[])
{
    char buf[256];
    char args[5][64];
    char* cmd[5] = {args[0], args[1], args[2], args[3], args[4]};
    bool batch = false;
    bool cache = false;
    char const* dbName = "stockdb.dbs";
    int cmdPos = argc;
    for (int i = 1; i < argc; i++) { 
        if (argv[i][0] == '-') { 
            switch (argv[i][1]) {
              case 'd':
                dbName = argv[++i];
                continue;
              case 'b':
                batch = true; 
                continue;
              case 'c':
                cache = true; 
                continue;
              default:
                fputs("Usage:\n\tstockdb [-d database_path] [-b] [-c] [command]\n", stderr);
                fputs(commandsList, stderr);
                return 1;
            }
        } else {
            cmdPos = i;
            break;
        }
    }
    YahooStockDataProvider yahoo(cache);
    StockDB db(&yahoo);
    if (db.open(dbName) != OK) { 
        fputs("Failed to open database\n", stderr);
        return 2;
    }
    if (cmdPos < argc) { 
        execCommand(db, argc-cmdPos, argv+cmdPos);
    } else {
        if (!batch) {
            printf("> ");
        }
        while (fgets(buf, sizeof(buf), stdin)) { 
            int n = sscanf(buf, "%s%s%s%s%s", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]);            
            if (n > 0) { 
                if (strcmp(cmd[0], "exit") == 0) { 
                    break;
                }
                if (execCommand(db, n, cmd) && !batch) { 
                    printf("Done\n");
                }
            }
            if (!batch) {
                printf("> ");
            }
        }
    }
    db.close();
    return 0;
}
            
    
        
